package uebung04_h;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.MemoryImageSource;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import tools.ColorConversions;
import tools.Gauss;
import tools.ShowImage;

public class Harris02 {
	
	public static double[] dx(double[] input, int w, int h, double[] output) {
		for (int i = 0; i<input.length; i++) {
			int x = i%w, y = i/w;
			int index = (x+w+1)%w+y*w;
			output[i] = -(input[i]-input[index]);
		}
		return output;
	}
	
	public static double[] dy(double[] input, int w, int h, double[] output) {
		for (int i = 0; i<input.length; i++) {
			int x = i%w, y = i/w;
			int index = x+((y+1+h)%h)*w;
			output[i] = input[i]-input[index];
		}
		return output;
	}
	public static int[] prepare(double[] input, int[] fillme) {
		double max = Double.NEGATIVE_INFINITY, min = Double.POSITIVE_INFINITY;
		for (int i = 0; i<input.length; i++) {
			max = Math.max(input[i], max);
			min = Math.min(input[i], min);
		}
		double span = max-min;
		for (int i = 0; i<input.length; i++) {
			fillme[i] = (int)(255.*((input[i]-min)/span));
		}
		return fillme;
	}
	public static int[] fuseRGB(int[] r, int[] g, int[] b, int[] fillme) {
		for (int i = 0; i<fillme.length; i++) {
			fillme[i] = r[i]<<16 | g[i]<<8 | b[i] | 0xFF000000;
		}
		return fillme;
	}
	
	public static void main(String[] args) {
		try {
			
			final int wResponse = 256, hResponse = 256, pixResponse[] = new int[wResponse*hResponse];
			
			final MemoryImageSource mis = new MemoryImageSource(wResponse, hResponse, pixResponse, 0, wResponse);
			
			mis.setAnimated(true);
			
			final JComponent canvas = new JComponent() {
				
				{setPreferredSize(new Dimension(wResponse,hResponse));}
				
				Image im = createImage(mis);
				
				@Override
				public void paint(Graphics g) {
					g.drawImage(im, 0, 0, this);
					super.paint(g);
				}
			};
			
			BufferedImage bi = ImageIO.read(Harris02.class.getClassLoader().getResourceAsStream("res/spaceinvaders.png"));
//			BufferedImage bi = ImageIO.read(Harris01.class.getClassLoader().getResourceAsStream("res/checkerrot.png"));
			
			final int w = bi.getWidth(), h = bi.getHeight();
			
			int[] pixels = bi.getRGB(0, 0, w, h, new int[w*h], 0, w);
			
			final int[] pix = bi.getRGB(0, 0, w, h, new int[w*h], 0, w);

			final MemoryImageSource mbi = new MemoryImageSource(w, h, pix, 0, w);
			mbi.setAnimated(true);
			
			final JComponent show = new JComponent() {
				{setPreferredSize(new Dimension(w,h));}
				Image im = createImage(mbi);
				@Override
				public void paint(Graphics g) {
					g.drawImage(im, 0, 0, this);
					super.paint(g);
				}
			};

			final JSlider sslider = new JSlider(JSlider.VERTICAL, 100, 100000, 10000);
			final JSlider jslider = new JSlider(JSlider.HORIZONTAL, 1, 250, 125);
			final JSlider nslider = new JSlider(JSlider.HORIZONTAL, 1, 20, 3);

			int[] lum = ColorConversions.getFastLuminance(pixels, new int[w*h]);
			int[] lumg = Gauss.gaussXY(lum, w, h, 1.6, new int[w*h]);
			double[] lumd = tools.Image.getScalarLuminance(lumg, new double[w*h]);
			
			
			final double[] dx = dx(lumd, w, h, new double[w*h]);
			final double[] dy = dy(lumd, w, h, new double[w*h]);
			
			final double[] Rs = new double[w*h];
			final double[] Rs_ = new double[w*h];
			
			ChangeListener cl = new ChangeListener() {
				
				int[] rint = new int[w*h], rant = new int[w*h], runt = new int[w*h];
				final double scale = 50/(double)wResponse;
				
				@Override
				public void stateChanged(ChangeEvent arg0) {
					int neighbours = nslider.getValue();
					double s = sslider.getValue()/1000., k = jslider.getValue()/1000., sigma = neighbours/6.;
					
					for (int y = 0; y<hResponse; y++) {
						for (int x = 0; x<wResponse; x++) {
							
							double X = scale*x, Y = y*scale, tr = X+Y, det = X*Y;
							double r = (det-k*tr*tr)*s;
							
							int rgb = 0;
							
							if (r < 0)
								rgb = (((int)-r)&0xFF) | 0xFF000000;
							else
								rgb = ((((int)r)<<16)&0xFF0000) | ((((int)r)&0x00FF00)<<1) | 0xFF000000;
							pixResponse[x+(hResponse-y-1)*wResponse] = rgb;
						}
					}
					
					mis.newPixels();
					
					for (int y = 0; y<h; y++) {
						for (int x = 0; x<w; x++) {
							
							double sum = 0, xx = 0, yx = 0, yy = 0;
							
							for (int j = -neighbours; j<neighbours; j++) {
								for (int i = -neighbours; i<neighbours; i++) {
									
									int index = ((x+i+w)%w)+((y+j+h)%h)*w;
									
									double dx_ = dx[index], dy_ = -dy[index], weight = 1;//Math.exp(-Math.sqrt(i*i+j*j)/(2*sigma*sigma));
									
									xx += dx_*dx_*weight;
									yx += dy_*dx_*weight;
									yy += dy_*dy_*weight;
									
									sum += weight;
								}
							}
//							System.out.println(sum);
//							xx /= sum;
//							yx /= sum;
//							yy /= sum;
							
							double val = ((xx*yy-yx*yx)-k*(xx+yy)*(xx+yy))*s;
							Rs[x+y*w] = val;
							Rs_[x+y*w] = -val;
						}
					}
					
					double maxrs = Double.NEGATIVE_INFINITY, min = Double.POSITIVE_INFINITY, maxRs_ = Double.NEGATIVE_INFINITY;
					
					for (int i = 0; i<Rs.length; i++) {
						if (Double.isNaN(Rs[i]))
							System.out.println("warning");
						min = Math.min(min, Rs[i]);
						maxrs = Math.max(maxrs, Rs[i] = Math.max(Rs[i], 0.1));
						maxRs_ = Math.max(Rs_[i] = Math.max(Rs_[i], 0.1), maxRs_);
					}
					
					rint = prepare(Rs, rint);
					rant = prepare(Rs_, rant);
					fuseRGB(rint, runt, rant, pix);
					
					mbi.newPixels();
					canvas.repaint();
					show.repaint();
				}
			};
			
			sslider.addChangeListener(cl);
			jslider.addChangeListener(cl);
			nslider.addChangeListener(cl);
			
			cl.stateChanged(new ChangeEvent(new Object()));
			
			JFrame ff = new JFrame();
			ff.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			ff.add(show, BorderLayout.CENTER);
			ff.add(canvas, BorderLayout.EAST);
			
			ff.add(sslider, BorderLayout.WEST);
			ff.add(jslider, BorderLayout.SOUTH);
			ff.add(nslider, BorderLayout.NORTH);
			
			ff.pack();
			ff.setVisible(true);
			
 			
		}
		catch (IOException iox) {
			iox.printStackTrace();
		}
		
	}
}
